package test.test.test;

import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXLayer;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXObject;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXObjectGroup;
import org.anddev.andengine.entity.layer.tiled.tmx.TMXTile;
import org.anddev.andengine.entity.layer.tiled.tmx.util.exception.TMXLoadException;
import org.anddev.andengine.entity.modifier.FadeInModifier;
import org.anddev.andengine.entity.modifier.FadeOutModifier;
import org.anddev.andengine.entity.modifier.IEntityModifier.IEntityModifierListener;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.modifier.IModifier;
import org.anddev.andengine.util.modifier.ease.EaseLinear;

import android.os.AsyncTask;

//This class is Used primarily for organization
public class TMXMapUpdater extends Game {
	
	// ===========================================================
	// Constants
	// ===========================================================
	

	
	// ===========================================================
	// Fields
	// ===========================================================

	//I kept all fields in the game class
	

	// ===========================================================
	// Constructors
	// ===========================================================
	
	public TMXMapUpdater() {
	}

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================
	
	
	// ===========================================================
	// Methods
	// ===========================================================
	
	public static void doProperty1(TMXTile PlayerTile) {
		//This needs to be called in the update handler

		//Do this property only on the tiles that the player steps on
		if (propertyTiles.contains(PlayerTile)) {

			int tempArrayIndex = propertyTiles.indexOf(PlayerTile);
			int tempIndex = propertyIndex.get(tempArrayIndex);
			//I just did something random
			if (tempIndex == 1) {
				PlayerTile.setGlobalTileID(mTMXTiledMap, PlayerTile.getGlobalTileID() - 1);
			}
		}
	}

	//***********************************************************************************************************************

	public static void doTileAnimations() {
		//cycles through the array and changes the global values
		//Note: you need to know the number of the tiles you want to cycle through
		for (int index = 0; index < animatedTiles.size(); index++) {

			TMXTile temptile = animatedTiles.get(index);

			//Controls the animation of the bush
			switch (temptile.getGlobalTileID()) {
			case 3:
				temptile.setGlobalTileID(mTMXTiledMap, 19);
				break;

			case 19:
				temptile.setGlobalTileID(mTMXTiledMap, 35);
				break;

			case 35:
				temptile.setGlobalTileID(mTMXTiledMap, 3);
				break;
			default:
				break;
			}

			//Controls the animation of the rock
			switch (temptile.getGlobalTileID()) {
			case 145:
				temptile.setGlobalTileID(mTMXTiledMap, 161);
				break;
				
			case 161:
				temptile.setGlobalTileID(mTMXTiledMap, 177);
				break;
				
			case 177:
				temptile.setGlobalTileID(mTMXTiledMap, 145);
				break;
			default:
				break;
			}

		}
	}

	//************************************************************************************************************************

	public static void fadeFromBlack(final float posX, final float posY) {

		FadeScreen.setPosition(posX - FadeScreen.getWidth() / 2, posY - FadeScreen.getHeight() / 2);

		/************
		 * THIS IS IMPORTANT************** The number indicates the Tiled layers
		 * you have in your map.It needs to be higher than the amount of layers
		 * you have on your scene. It can't be too high either otherwise the
		 * screen will not respond. So far this solution works
		 */
		mScene.attachChild(FadeScreen, mScene.getChildCount());

		//Sets up the fade properties
		FadeOutModifier prFadeOutModifier = new FadeOutModifier(1f, new IEntityModifierListener() {
			@Override
			public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {

				/*
				 * This controls the walking animation after the scene has
				 * loaded The reason I used numbers is because switch statements
				 * do not support strings. If you look at your num pad I think
				 * it should make sense
				 */
				switch (playerSpawnDirection) {
				case 4:
					//LEFT
					UI.walkTo(posX - TILE_WIDTH, posY, mScene);
					break;

				case 6:
					//RIGHT
					UI.walkTo(posX + TILE_WIDTH, posY, mScene);
					break;

				case 2:
					//DOWN
					UI.walkTo(posX, posY + TILE_HEIGHT, mScene);
					break;

				case 8:
					//UP
					UI.walkTo(posX, posY - TILE_HEIGHT, mScene);
					break;

				default:
					break;
				}

				//Fades the controller in
				if (Options.DigitalControlEnabled) {
					mDigitalOnScreenControl.registerEntityModifier(new FadeInModifier(0.5f, EaseLinear.getInstance()));
				}

			}

			@Override
			public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
				//Once the scene has faded to the scene then the map has finished loading
				//See OnSceneTouchEvent and the updateHandler to see what this controls
				MapIsLoading = false;
				
				//Allows the DPad to be used again
				if (Options.DigitalControlEnabled) {
					mDigitalOnScreenControl.setIgnoreUpdate(false);
				}

			}
		}, EaseLinear.getInstance());

		//Apply the fade properties
		FadeScreen.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		FadeScreen.registerEntityModifier(prFadeOutModifier);

	}

	//************************************************************************************************************************

	public static void fadeToBlack() {

		FadeScreen.setPosition(playerLocationTile.getTileX() - FadeScreen.getWidth() / 2, playerLocationTile.getTileY()
				- FadeScreen.getHeight() / 2);

		//Sets up the fade properties
		FadeInModifier prFadeInModifier = new FadeInModifier(1f, new IEntityModifierListener() {
			@Override
			public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
				if (Options.DigitalControlEnabled) {
					mDigitalOnScreenControl.setIgnoreUpdate(true);
					mScene.clearChildScene();
					//TODO:Why does this not work. It should fade
					mDigitalOnScreenControl.registerEntityModifier(new FadeOutModifier(0.5f, EaseLinear.getInstance()));
					//TODO: This is too choppy and you can see that the sprite does not animate
					player.stopAnimation();
				}

			}

			@Override
			public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
				/*
				 * The first index is the position in the array the Exit tile,
				 * at the players feet, is located at.The second index is the
				 * map number associated with the first Index
				 */
				int tempTransIndex = ExitTiles.indexOf(playerLocationTile);
				int tempMapIndex = MapNumber.get(tempTransIndex);
				if (LinkNumber.get(tempTransIndex) != null)
					mLinkIndex = LinkNumber.get(tempTransIndex);
				//loads & attaches the assets for the next map
				loadAndAttachAssets(tempMapIndex);

				mScene.detachChild(FadeScreen);

			}
		}, EaseLinear.getInstance());

		//Apply the fade properties
		FadeScreen.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		FadeScreen.registerEntityModifier(prFadeInModifier);

	}

	//*******************************************************************************************************************

	public static void getPropertyTiles() {
		propertyTiles = new ArrayList<TMXTile>();
		propertyIndex = new ArrayList<Integer>();
		// The for loop cycles through each object on the map
		for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

			// Gets the object layer with these properties. Use if you have many object layers, Otherwise this can be removed
			if (group.getTMXObjectGroupProperties().get(0).getName().equals("Property")) {

				for (final TMXObject object : group.getTMXObjects()) {
					ObjectX = object.getX() + TILE_WIDTH / 2;
					ObjectY = object.getY() + TILE_HEIGHT / 2;
					// Gets the number of rows and columns in the object
					ObjectHeight = object.getHeight() / TILE_HEIGHT;
					ObjectWidth = object.getWidth() / TILE_WIDTH;

					// Gets the tiles the object covers and puts it into the Arraylist CollideTiles
					for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
						for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
							TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
									+ TileRow * TILE_HEIGHT);

							propertyTiles.add(tempTile);
							propertyIndex.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(0).getValue()));
						}
					}

				}
			}

		}
	}

	//*********************************************************************************************************************

	public static void getAnimatedTiles() {
		animatedTiles = new ArrayList<TMXTile>();

		for (int TileRow = 0; TileRow < mAnimateLayer.getTileRows(); TileRow++) {
			for (int TileColumn = 0; TileColumn < mAnimateLayer.getTileColumns(); TileColumn++) {
				TMXTile tempTile = mAnimateLayer.getTMXTileAt(TILE_WIDTH / 2 + TileColumn * TILE_WIDTH, TILE_HEIGHT / 2
						+ TileRow * TILE_HEIGHT);
				if (tempTile != null)
					animatedTiles.add(tempTile);
			}
		}
	}

	//*********************************************************************************************************************
	
	public static void getCollideTiles(){
		/*
		 * Determines the tiles that have the Object layer
		 */
		// Uses an array list because the size will need to be
		// changed dynamically.
		CollideTiles = new ArrayList<TMXTile>();
		// The for loop cycles through each object on the map
		for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

			// Gets the object layer with these properties. Use if
			// you have many object layers, Otherwise this can be
			// removed
			if (group.getTMXObjectGroupProperties().containsTMXProperty("Collide", "true")) {

				for (final TMXObject object : group.getTMXObjects()) {

					ObjectX = object.getX() + TILE_WIDTH / 2;
					ObjectY = object.getY() + TILE_HEIGHT / 2;
					// Gets the number of rows and columns in the
					// object
					ObjectHeight = object.getHeight() / TILE_HEIGHT;
					ObjectWidth = object.getWidth() / TILE_WIDTH;

					// Gets the tiles the object covers and puts it
					// into the Arraylist CollideTiles
					for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
						for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
							TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
									+ TileRow * TILE_HEIGHT);
							CollideTiles.add(tempTile);
						}
					}

				}
			}

		}
	}
	
	//*********************************************************************************************************************

	public static void getPlayerSpawnTiles() {
		playerSpawnTiles = new ArrayList<TMXTile>();

		// The for loop cycles through each object on the map
		for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

			// Gets the object layer with these properties. Use if you have many object layers, Otherwise this can be removed
			if (group.getTMXObjectGroupProperties().containsTMXProperty("Exit", Integer.toString(mCurrentMapIndex))) {

				for (final TMXObject object : group.getTMXObjects()) {
					ObjectX = object.getX() + TILE_WIDTH / 2;
					ObjectY = object.getY() + TILE_HEIGHT / 2;
					// Gets the number of rows and columns in the
					// object
					ObjectHeight = object.getHeight() / TILE_HEIGHT;
					ObjectWidth = object.getWidth() / TILE_WIDTH;

					// Gets the tiles the object covers and puts it into the Arraylist CollideTiles
					for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
						for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
							TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
									+ TileRow * TILE_HEIGHT);

							//If the tile has a specific link then go to the specific location else go to the only location
							if (group.getTMXObjectGroupProperties().containsTMXProperty("Link",
									Integer.toString(mLinkIndex))) {

								playerSpawnTiles.add(tempTile);
								//The second property needs to be the player direction he will be walking in when he enters the map
								playerSpawnDirection = Integer.parseInt(group.getTMXObjectGroupProperties().get(2)
										.getValue());

							} else if (group.getTMXObjectGroupProperties().size() < 3) {
								playerSpawnTiles.add(tempTile);
								//This works if there are only two properties
								playerSpawnDirection = Integer.parseInt(group.getTMXObjectGroupProperties().get(1)
										.getValue());

							}

						}
					}

				}
			}

		}
	}

	//*********************************************************************************************************************

	public static void getExitTiles() {
		// Uses an array list because the size will need to be changed dynamically.
		ExitTiles = new ArrayList<TMXTile>();
		MapNumber = new ArrayList<Integer>();
		LinkNumber = new ArrayList<Integer>();
		// The for loop cycles through each object on the map
		for (final TMXObjectGroup group : mTMXTiledMap.getTMXObjectGroups()) {

			/*
			 * Gets the object layer with these properties. Use if you have many
			 * object layers, Otherwise this can be removed
			 */
			if (group.getTMXObjectGroupProperties().get(0).getName().equals("Exit")) {

				for (final TMXObject object : group.getTMXObjects()) {

					ObjectX = object.getX() + TILE_WIDTH / 2;
					ObjectY = object.getY() + TILE_HEIGHT / 2;
					// Gets the number of rows and columns in the object
					ObjectHeight = object.getHeight() / TILE_HEIGHT;
					ObjectWidth = object.getWidth() / TILE_WIDTH;

					// Gets the tiles the object covers and puts it into the Arraylist CollideTiles					
					for (int TileRow = 0; TileRow < ObjectHeight; TileRow++) {
						for (int TileColumn = 0; TileColumn < ObjectWidth; TileColumn++) {
							TMXTile tempTile = TMXMapLayer.getTMXTileAt(ObjectX + TileColumn * TILE_WIDTH, ObjectY
									+ TileRow * TILE_HEIGHT);
							ExitTiles.add(tempTile);
							MapNumber.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(0).getValue()));

							//This means that the group has the link property in it
							if (group.getTMXObjectGroupProperties().size() >= 3) {
								LinkNumber.add(Integer.parseInt(group.getTMXObjectGroupProperties().get(1).getValue()));
								/*
								 * you need to know the order of your
								 * properties. Use the following if you are not
								 * sure, and check logCat:
								 * Log.i("Link", group.getTMXObjectGroupProperties().get(1).getValue());
								 */
							} else {
								//fills the array to match ExitTiles
								LinkNumber.add(null);
							}
						}
					}

				}
			}

		}
	}
	
	//*********************************************************************************************************************

	public static void updateTMXMap() {

		if (ExitTiles.contains(playerLocationTile) && playerLocationTile.getTileX() % 32 == 0
				&& playerLocationTile.getTileY() % 32 == 0) {

			/*
			 * if on the exit tile, the player won't be able to interact with
			 * the screen and the update will stop until the tmx has loaded.
			 * Once the load has finished the player will appear on the new map
			 * off of the exit tile and the MapIsLoading variable will return
			 * false
			 */
			MapIsLoading = true;
			UpdateTiles = false;
			//Ends the path at the tile the sprite is on
			UI.walkTo(playerLocationTile.getTileX(), playerLocationTile.getTileY(), mScene);

			//Contains the TMXMap loader in onModifierFinished
			fadeToBlack();

		}
	}

	//********************************************************************************************************************

	public static void getTMXMap(int MapIndex) {
		//If the index stored from the large map equals the map index being called than use the map that has been saved
		//This cuts the loading time down for large maps but increases the amount of memory used
		if (mLargeTMXIndex == MapIndex) {
			mTMXTiledMap = mLargeTMXMap;
		} else {

			switch (MapIndex) {
			case 0:
				// This is the initial map the player spawns on
				try {
					mTMXTiledMap = tmxLoader.loadFromAsset(mContext, "tmx/test.tmx");
				} catch (final TMXLoadException tmxle) {
					Debug.e(tmxle);
				}

				break;

			case 1:
				//This is the large 200x200 tiled map. It usually takes 8 to 30 seconds to load depending on the device
				try {
					mTMXTiledMap = tmxLoader.loadFromAsset(mContext, "tmx/Route.tmx");
					//Put this in any large TMX you want to keep in memory. This will store the most recent one
					mLargeTMXIndex = MapIndex;
					//Save the recently loaded large map in memory
					mLargeTMXMap = mTMXTiledMap;
				} catch (final TMXLoadException tmxle) {
					Debug.e(tmxle);
				}
				break;

			case 2:
				//This is the first level of the building
				try {
					mTMXTiledMap = tmxLoader.loadFromAsset(mContext, "tmx/Building1.tmx");
				} catch (final TMXLoadException tmxle) {
					Debug.e(tmxle);
				}
				break;

			case 3:
				// this is the second floor of the building
				try {
					mTMXTiledMap = tmxLoader.loadFromAsset(mContext, "tmx/Building2.tmx");
				} catch (final TMXLoadException tmxle) {
					Debug.e(tmxle);
				}
				break;
			default:

				break;
			}
		}
	}

	//*********************************************************************************************************************

	public static void loadAndAttachAssets(final int MapIndex) {

		//Use AsyncTask to load TMX map from a saved state				 
		new AsyncTask<Void, Integer, Void>() {

			@Override
			protected void onPreExecute() {
				//Clears the scene for the next map
				super.onPreExecute();
				mScene.clearEntityModifiers();
				mScene.detachChildren();
				A_path = null;
				//Clears all layers of their values
				mAnimateLayer = null;
				mTopLayer = null;
			}

			//Set what gets done in the background
			@Override
			protected Void doInBackground(Void... params) {

				/*
				 * MapIsLoading is needed here because the initial load can be
				 * anywhere not just on an exit tile. SEE updateTMXMap() method
				 */
				MapIsLoading = true;

				// Load the TMX map
				getTMXMap(MapIndex);

				AssetsAttached = false;

				return null;
			}

			//Set what gets done after doInBackgroung ends 
			@Override
			protected void onPostExecute(Void result) {
				System.gc();
				AttachAssets();

			}

		}.execute();
	}

	//**********************************************************************************************************************	

	public static void AttachAssets() {
		/*
		 * Attaches the layers for this map and sets them to be invisible.
		 * Explained below.
		 */
		if (!AssetsAttached) {
			for (int LayerIndex = 0; LayerIndex < mTMXTiledMap.getTMXLayers().size(); LayerIndex++) {
				TMXLayer layer = mTMXTiledMap.getTMXLayers().get(LayerIndex);
				if (layer.getTMXLayerProperties().containsTMXProperty("Top", "true")) {
					//This layer is attached later
					layer.setVisible(false);
					mTopLayer = layer;
				}
				if (layer.getTMXLayerProperties().containsTMXProperty("Animate", "true")) {
					mAnimateLayer = layer;
				}
				if (!layer.getTMXLayerProperties().containsTMXProperty("Top", "true")) {
					layer.setVisible(false);
					mScene.attachChild(layer);
				}
			}

			// Layer initialization
			TMXMapLayer = mTMXTiledMap.getTMXLayers().get(0);

			//Gets the tiles which activate special events
			getExitTiles();
			getPlayerSpawnTiles();
			getPropertyTiles();
			getCollideTiles();
			if (mAnimateLayer != null) {
				getAnimatedTiles();
			}

			//Gets the spawn location of the player sprite
			float playerX;
			float playerY;
			if (playerSpawnTiles.size() > 0) {
				playerX = playerSpawnTiles.get(0).getTileX();
				playerY = playerSpawnTiles.get(0).getTileY();

			} else {
				playerX = InitialplayerSpawnX;
				playerY = InitialplayerSpawnY;

			}

			// Add player sprite to the scene with the chase camera on the player sprite
			// Create the player sprite at the specified location
			player = new AnimatedSprite(playerX, playerY, mPlayerTextureRegion);
			player.setVisible(false);
			mScene.attachChild(player);

			//Attach the top layer, if it is on the map, after the player is added so that the player can travel under it
			if (mTopLayer != null)
				mScene.attachChild(mTopLayer);

			/*
			 * This activates the ability to Update the player location on the
			 * tiles. It is used in the UpdateHandler. This enables the walking
			 * effect when a scene changes, since the walkto method requires the
			 * players current tile to make a path
			 */
			UpdateTiles = true;

			// Make the camera not exceed the bounds of the TMXEntity. Sets the Chase entity and Sets the center of the camera
			mBoundChaseCamera.setCenter(playerX, playerY);
			mBoundChaseCamera.setChaseEntity(player);
			mBoundChaseCamera.setBounds(0, TMXMapLayer.getWidth(), 0, TMXMapLayer.getHeight());
			mBoundChaseCamera.setBoundsEnabled(EnableBounds);

			//Depending on the option settings the Dpad or touch will be enabled
			UI.GeneralUI(Options.TouchEnabled, Options.DigitalControlEnabled);

			//Only attaches the assets once
			AssetsAttached = true;

			/*
			 * Make all layers visible right before fading in. If you know the
			 * layers you are using then it may be easier to just add them
			 * manuallyThis is used to prevent the flickering caused by the
			 * moving camera. This also ensures a smooth transition since
			 * everything has been loaded
			 */
			for (int LayerIndex = 0; LayerIndex < mTMXTiledMap.getTMXLayers().size(); LayerIndex++) {
				TMXLayer layer = mTMXTiledMap.getTMXLayers().get(LayerIndex);
				layer.setVisible(true);

			}
			player.setVisible(true);

			//Calls the fade method. See above for method details.
			fadeFromBlack(playerX, playerY);

			//Removes the detached layers 
			System.gc();
		}

	}
	

}
	




